#pragma once

#include "vec2.h"

template <class treal>
class basevec3
{
public:
    basevec3()
        :x(0), y(0), z(0)
    {}

    explicit basevec3(const treal *p)
        :x(p[0]), y(p[1]), z(p[2])
    {}

    explicit basevec3(basevec2<treal> const& vec, treal const& z0)
        :x(vec.x), y(vec.y), z(z0)
    {}

    explicit basevec3(treal const& x0, treal const& y0, treal const& z0)
        :x(x0), y(y0), z(z0)
    {}

    inline operator treal const*() const
    {
        return &x;
    }

    inline operator treal*()
    {
        return &x;
    }

    inline basevec3 & operator *= (treal const& scale)
    {
        x *= scale;
        y *= scale;
        z *= scale;
        return *this;
    }

    inline basevec3 & operator /= (treal const& scalar)
    {
        return *this *= (1 / scalar);
    }

    inline basevec3 const operator*(treal const & scale)const
    {
        return basevec3(x * scale, y * scale, z * scale);
    }

    inline basevec3 const operator-(basevec3 const& v)const
    {
        return basevec3(x - v.x, y - v.y, z - v.z);
    }

    inline basevec3 const operator-()const
    {
        return basevec3(-x, -y, -z);
    }

    inline basevec3 const operator+(basevec3 const& v)const
    {
        return basevec3(x + v.x, y + v.y, z + v.z);
    }

    inline basevec3 & operator+=(basevec3 const& v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
        return *this;
    }

    inline treal getLength()const
    {
        return static_cast<treal>(sqrt(x * x + y * y + z * z));
    }

    inline void normalize()
    {
        treal invLength = 1 / getLength();
        x *= invLength;
        y *= invLength;
        z *= invLength;
    }

    treal x;
    treal y;
    treal z;
};

template <class treal> inline
treal dot(basevec3<treal> const& a, basevec3<treal> const& b)
{
    return (a.x * b.x + a.y * b.y + a.z * b.z);
}

template <class treal> inline
basevec3<treal> const cross(basevec3<treal> const & a, basevec3<treal> const & b)
{
    /*
          |i  j  k |
    AxB = |Ax Ay Az|
          |Bx By Bz|
    */
    return basevec3<treal>(
        a.y * b.z - a.z * b.y,
        a.z * b.x - a.x * b.z,
        a.x * b.y - a.y * b.x );

}

template <class treal> inline
basevec3<treal> operator * (treal const& scale, basevec3<treal> vec)
{
    return basevec3<treal>(vec.x * scale,
                           vec.y * scale,
                           vec.z * scale);
}

typedef basevec3<float> vec3f;
typedef basevec3<double> vec3d;
